#ifndef __BUOLA_GUI_CMENUITEM_H__
#define __BUOLA_GUI_CMENUITEM_H__

#include <buola/gui.h>
#include <buola/gui/cicon.h>
#include <buola/utility/uevent.h>
#include <buola/utility/uvalue.h>
#include <set>

namespace buola { namespace gui {

///\addtogroup gui
///@{

////////////////////////////////////////////////////////////////////////////

/// menu item

///
///
///
////////////////////////////////////////////////////////////////////////////

class CMenuItem : public UShared
{
private:
    //! item type
    enum EType
    {
        ITEM,
        SUBMENU,
        ITEM_SUBMENU,
        SEPARATOR,
        LABEL,
        WINDOW,
        INLINE
    };

    //! item flags
    enum EFlags
    {
        FLAG_NONE=0,
        FLAG_DEFAULT=(1<<0),
        FLAG_CHECKABLE=(1<<1),
        FLAG_CHECKED=(1<<2),
        FLAG_HIDDEN=(1<<3),
        FLAG_DISABLED=(1<<4)
    };

    DECLARE_FRIEND_ENUM_BITOPS(EFlags)

    struct SGroup
    {
        std::set<CMenuItem*> mItems;
    };

private:
//! \name constructor and destructor
//! can only be used from friend constructor functions below
//@{
    CMenuItem(EType pType,PMenu pSubMenu,const std::wstring &pText=std::wstring(),PIcon pIcon=nullptr,
              const std::wstring &pComment=std::wstring());
    CMenuItem(EType pType,const PWindow &pWindow);
    CMenuItem(EType pType,const std::wstring &pText=std::wstring(),PIcon pIcon=nullptr,CAccelerator *pAccelerator=nullptr,
              const std::wstring &pComment=std::wstring());
    ~CMenuItem();
//@}

public:
//! \name public interface
//@{
    bool IsNormalItem()         {   return (mType==ITEM);       }
    bool IsItem()               {   return (mType==ITEM||mType==ITEM_SUBMENU);  }
    bool IsSubMenu()            {   return (mType==SUBMENU);    }
    bool IsItemWithSubMenu()    {   return (mType==ITEM_SUBMENU);   }
    bool IsSeparator()          {   return (mType==SEPARATOR);  }
    bool IsLabel()              {   return (mType==LABEL);      }
    bool IsWindow()             {   return (mType==WINDOW);     }
    bool IsInlineMenu()         {   return (mType==INLINE);     }

    bool IsCheckable()          {   return GetFlag(FLAG_CHECKABLE); }
    bool IsDefault()            {   return GetFlag(FLAG_DEFAULT);   }
    bool IsHidden()             {   return GetFlag(FLAG_HIDDEN);   }
    bool IsDisabled()           {   return GetFlag(FLAG_DISABLED);   }
    
    bool IsChecked()            {   return GetFlag(FLAG_CHECKED);   }

    //! returns the submenu (for items of type SUBMENU,ITEM_SUBMENU or INLINE)
    PMenu GetSubMenu()              {   return mSubMenu;        }
    const std::wstring &GetText()       {   return mText;           }
    const std::wstring &GetComment()    {   return mComment;        }
    PIcon GetIcon()                 {   return mIcon;           }
    const UValue &GetUserData()     {   return mUserData;       }
    const PWindow &GetWindow()      {   return mWindow;         }
    const std::wstring &GetAccelText()  {   return mAccelText;      }
    
    void SetCheckable(bool pCheckable=true);
    void SetDefault(bool pDefault=true);
    void SetHidden(bool pHidden=true);
    void SetDisabled(bool pDisabled=true);
    
    void GroupWith(PMenuItem pOther);

    //! called when the item has been selected in a menu(triggers the event)
    void Trigger();
    void Check(bool pChecked=true);

private:
    void AddMenu(CMenu *pMenu);
    void RemoveMenu(CMenu *pMenu);
    
    //! set a flag
    void SetFlag(EFlags pFlag)      {   mFlags|=pFlag;      }
    //! unset a flag
    void UnsetFlag(EFlags pFlag)    {   mFlags&=(~pFlag);   }
    //! check whether a flag is set
    bool GetFlag(EFlags pFlag)      {   return ((mFlags&pFlag)==pFlag); }

    void OnChange();

public:
    //! event which gets triggered when the item is activated
    UEvent<void()> eTriggered;

private:
    EType mType;            //!< type of item

    std::wstring mText;         //!< text to be displayed
    std::wstring mComment;      //!< comment (as should appear on a tooltip)
    PIcon mIcon;            //!< icon to be displayed
    UValue mUserData;

    PMenu mSubMenu;         //!< submenu

    SGroup *mGroup;             //!< item group
    std::wstring mAccelText;    //!< text of the linked accelerator
    
    PWindow mWindow;       //!< window (for items of type WINDOW)

    EFlags mFlags;          //!< flags

    std::set<CMenu*> mMenus;    //!< menus this item appears in

    friend class CMenu;
    friend PMenuItem new_menu_item(const std::wstring &pText,PIcon pIcon,CAccelerator *pAccelerator,
                                   const std::wstring &pComment);
    friend PMenuItem new_menu_item(const std::wstring &pText,const std::string &pIcon,CAccelerator *pAccelerator,
                                   const std::wstring &pComment);
    friend PMenuItem new_checkable_menu_item(const std::wstring &pText,PIcon pIcon,
                                             CAccelerator *pAccelerator,const std::wstring &pComment);
    friend PMenuItem new_checkable_menu_item(const std::wstring &pText,const std::string &pIcon,
                                             CAccelerator *pAccelerator,const std::wstring &pComment);
    friend PMenuItem new_checkable_menu_item(PMenuItem pGroupWith,const std::wstring &pText,PIcon pIcon,
                                             CAccelerator *pAccelerator,const std::wstring &pComment);
    friend PMenuItem new_checkable_menu_item(PMenuItem pGroupWith,const std::wstring &pText,const std::string &pIcon,
                                             CAccelerator *pAccelerator,const std::wstring &pComment);
    friend PMenuItem new_menu_label(const std::wstring &pText,PIcon pIcon);
    friend PMenuItem new_submenu(const std::wstring &pText,PIcon pIcon,PMenu pSubMenu,const std::wstring &pComment);
};

inline PMenuItem new_menu_item(const std::wstring &pText,PIcon pIcon=nullptr,CAccelerator *pAccelerator=nullptr,
                               const std::wstring &pComment=std::wstring())
{
    return new CMenuItem(CMenuItem::ITEM,pText,pIcon,pAccelerator,pComment);
}

inline PMenuItem new_menu_item(const std::wstring &pText,const std::string &pIcon,CAccelerator *pAccelerator=nullptr,
                               const std::wstring &pComment=std::wstring())
{
    return new CMenuItem(CMenuItem::ITEM,pText,CIcon::Get(pIcon),pAccelerator,pComment);
}

inline PMenuItem new_checkable_menu_item(const std::wstring &pText,PIcon pIcon=nullptr,CAccelerator *pAccelerator=nullptr,
                               const std::wstring &pComment=std::wstring())
{
    PMenuItem lItem=new CMenuItem(CMenuItem::ITEM,pText,pIcon,pAccelerator,pComment);
    lItem->SetCheckable();
    return lItem;
}
                               
inline PMenuItem new_checkable_menu_item(const std::wstring &pText,const std::string &pIcon,CAccelerator *pAccelerator=nullptr,
                               const std::wstring &pComment=std::wstring())
{
    PMenuItem lItem=new CMenuItem(CMenuItem::ITEM,pText,CIcon::Get(pIcon),pAccelerator,pComment);
    lItem->SetCheckable();
    return lItem;
}

inline PMenuItem new_checkable_menu_item(PMenuItem pGroupWith,const std::wstring &pText,PIcon pIcon=nullptr,
                                         CAccelerator *pAccelerator=nullptr,const std::wstring &pComment=std::wstring())
{
    PMenuItem lItem=new CMenuItem(CMenuItem::ITEM,pText,pIcon,pAccelerator,pComment);
    lItem->SetCheckable();
    lItem->GroupWith(pGroupWith);
    return lItem;
}

inline PMenuItem new_checkable_menu_item(PMenuItem pGroupWith,const std::wstring &pText,const std::string &pIcon,
                                         CAccelerator *pAccelerator=nullptr,const std::wstring &pComment=std::wstring())
{
    PMenuItem lItem=new CMenuItem(CMenuItem::ITEM,pText,CIcon::Get(pIcon),pAccelerator,pComment);
    lItem->SetCheckable();
    lItem->GroupWith(pGroupWith);
    return lItem;
}

inline PMenuItem new_menu_label(const std::wstring &pText,PIcon pIcon=nullptr)
{
    return new CMenuItem(CMenuItem::LABEL,pText,pIcon);
}

inline PMenuItem new_submenu(const std::wstring &pText,PIcon pIcon=nullptr,PMenu pSubMenu=nullptr,
                             const std::wstring &pComment=std::wstring())
{
    return new CMenuItem(CMenuItem::SUBMENU,pSubMenu,pText,pIcon,pComment);
}

///@}

/*namespace gui*/ } /*namespace buola*/ }

#endif
